package org.chaiyi.template.core.framework.swagger;

import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import lombok.Builder;
import lombok.Data;
import org.chaiyi.template.core.common.exception.BaseErrorCode;
import org.springframework.http.HttpMethod;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

@Data
@Builder
public class OperationApiInfo {
    private String tag;
    private String path;
    private HttpMethod method;
    private String summary;
    private String operationId;
    private List<Parameter> parameters;
    private boolean reqIsArray;
    private boolean resIsArray;
    private Class<?> requestBody;
    private Class<?> responseBody;

    public OperationApiInfo addParameter(Parameter parameter) {
        doubleLock();
        parameters.add(parameter);
        return this;
    }

    private void doubleLock() {
        if (Objects.isNull(parameters)) {
            synchronized (this) {
                if (Objects.isNull(parameters)) {
                    this.parameters = new CopyOnWriteArrayList<>();
                }
            }
        }
    }

    public OperationApiInfo addParameters(List<Parameter> parameters) {
        doubleLock();
        this.parameters.addAll(parameters);
        return this;
    }

    public PathItem buildPathItem(OpenAPI openAPI) {
        PathItem pathItem = openAPI.getPaths().get(path);
        boolean isNull = Objects.isNull(pathItem);

        pathItem = isNull ? new PathItem() : pathItem;

        Operation operation = new Operation();
        operation.parameters(parameters)
                .summary(summary)
                .addTagsItem(tag)
                .operationId(operationId)
                .requestBody(buildRequestBody())
                .responses(buildResponse());

        if (method.equals(HttpMethod.GET)) {
            pathItem.get(operation);
        } else if (method.equals(HttpMethod.POST)) {
            pathItem.post(operation);
        } else if (method.equals(HttpMethod.PUT)) {
            pathItem.put(operation);
        } else if (method.equals(HttpMethod.DELETE)) {
            pathItem.delete(operation);
        } else {
            BaseErrorCode.SORT_ITEM_EXCEPTION.throwException();
        }

        if (isNull) {
            openAPI.path(path, pathItem);
        }
        return pathItem;
    }

    private ApiResponses buildResponse() {
        if (Objects.isNull(responseBody)) return null;
        Schema schema = ModelConverters.getInstance().resolveAsResolvedSchema(new AnnotatedType(responseBody)).schema;
        return new ApiResponses().addApiResponse("200", new ApiResponse().content(
                new Content().addMediaType("*/*",
                        new MediaType().schema(reqIsArray ? new ArraySchema().items(schema) : schema))));
    }

    private RequestBody buildRequestBody() {
        if (Objects.isNull(requestBody)) return null;
        Schema schema = ModelConverters.getInstance().resolveAsResolvedSchema(new AnnotatedType(requestBody)).schema;
        return new RequestBody()
                .content(new Content().addMediaType("application/json",
                        new MediaType().schema(reqIsArray ? new ArraySchema().items(schema) : schema)));
    }
}
